﻿' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Test.Utilities

Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE

    Public Class HasUnsupportedMetadata : Inherits BasicTestBase

        <Fact()>
        Public Sub Test1()

            Dim iLSource =
            <![CDATA[
.assembly extern NotReferenced {}

.class public auto ansi sealed D1`1<T>
       extends [mscorlib]System.MulticastDelegate
{
  .method public specialname rtspecialname 
          instance void  .ctor(object TargetObject,
                               native int TargetMethod) runtime managed
  {
  } // end of method D1`1::.ctor

  .method public newslot strict virtual instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(!T x,
                      class [mscorlib]System.AsyncCallback DelegateCallback,
                      object DelegateAsyncState) runtime managed
  {
  } // end of method D1`1::BeginInvoke

  .method public newslot strict virtual instance void 
          EndInvoke(class [mscorlib]System.IAsyncResult DelegateAsyncResult) runtime managed
  {
  } // end of method D1`1::EndInvoke

  .method public newslot strict virtual instance void 
          Invoke(!T x) runtime managed
  {
  } // end of method D1`1::Invoke

} // end of class D1`1

.class public auto ansi C2`1<T>
       extends [mscorlib]System.Object
{
  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method C2`1::.ctor

} // end of class C2`1

.class public auto ansi C3
       extends class C2`1<class [NotReferenced]C1[]>
{
  .field public class C2`1<class [NotReferenced]C1[]> F1
  .field private class D1`1<class [NotReferenced]C1[]> E1Event
  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void class C2`1<class [NotReferenced]C1[]>::.ctor()
    IL_0006:  ret
  } // end of method C3::.ctor

  .method public instance void  M1(class C2`1<class [NotReferenced]C1[]> x) cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method C3::M1

  .method public specialname instance void 
          add_E1(class D1`1<class [NotReferenced]C1[]> obj) cil managed synchronized
  {
    // Code size       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class D1`1<class [NotReferenced]C1[]> C3::E1Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                            class [mscorlib]System.Delegate)
    IL_000d:  castclass  class D1`1<class [NotReferenced]C1[]>
    IL_0012:  stfld      class D1`1<class [NotReferenced]C1[]> C3::E1Event
    IL_0017:  ret
  } // end of method C3::add_E1

  .method public specialname instance void 
          remove_E1(class D1`1<class [NotReferenced]C1[]> obj) cil managed synchronized
  {
    // Code size       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class D1`1<class [NotReferenced]C1[]> C3::E1Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
                                                                                           class [mscorlib]System.Delegate)
    IL_000d:  castclass  class D1`1<class [NotReferenced]C1[]>
    IL_0012:  stfld      class D1`1<class [NotReferenced]C1[]> C3::E1Event
    IL_0017:  ret
  } // end of method C3::remove_E1

  .method public specialname instance class C2`1<class [NotReferenced]C1[]> 
          get_P1() cil managed
  {
    // Code size       6 (0x6)
    .maxstack  1
    .locals init (class C2`1<class [NotReferenced]C1[]> V_0)
    IL_0000:  ldnull
    IL_0001:  stloc.0
    IL_0002:  br.s       IL_0004

    IL_0004:  ldloc.0
    IL_0005:  ret
  } // end of method C3::get_P1

  .method public specialname instance void 
          set_P1(class C2`1<class [NotReferenced]C1[]> 'value') cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method C3::set_P1

  .event class D1`1<class [NotReferenced]C1[]> E1
  {
    .addon instance void C3::add_E1(class D1`1<class [NotReferenced]C1[]>)
    .removeon instance void C3::remove_E1(class D1`1<class [NotReferenced]C1[]>)
  } // end of event C3::E1
  .property instance class C2`1<class [NotReferenced]C1[]>
          P1()
  {
    .get instance class C2`1<class [NotReferenced]C1[]> C3::get_P1()
    .set instance void C3::set_P1(class C2`1<class [NotReferenced]C1[]>)
  } // end of property C3::P1
} // end of class C3
]]>


            Dim compilation1 = CompilationUtils.CreateCompilationWithCustomILSource(
    <compilation name="Test">
        <file name="a.vb">
        </file>
    </compilation>, iLSource)

            Dim c3 = compilation1.GetTypeByMetadataName("C3")

            Assert.Null(c3.GetUseSiteErrorInfo())
            Assert.False(c3.HasUnsupportedMetadata)
            Assert.False(c3.ContainingSymbol.HasUnsupportedMetadata)

            Dim f1 = c3.GetMembers("F1").Single()
            Assert.NotNull(f1.GetUseSiteErrorInfo())
            Assert.False(f1.HasUnsupportedMetadata)

            Dim m1 = c3.GetMembers("M1").Single()
            Assert.NotNull(m1.GetUseSiteErrorInfo())
            Assert.False(m1.HasUnsupportedMetadata)

            Dim x = DirectCast(m1, MethodSymbol).Parameters(0)
            Assert.Null(x.GetUseSiteErrorInfo())
            Assert.False(x.HasUnsupportedMetadata)

            Dim e1 = c3.GetMembers("E1").Single()
            Assert.NotNull(e1.GetUseSiteErrorInfo())
            Assert.False(e1.HasUnsupportedMetadata)

            Dim p1 = c3.GetMembers("P1").Single()
            Assert.NotNull(p1.GetUseSiteErrorInfo())
            Assert.False(p1.HasUnsupportedMetadata)
        End Sub

        <Fact()>
        Public Sub Test2()

            Dim iLSource =
            <![CDATA[
.assembly extern NotReferenced {}

.class public auto ansi sealed D1`1<T>
       extends [mscorlib]System.MulticastDelegate
{
  .method public specialname rtspecialname 
          instance void  .ctor(object TargetObject,
                               native int TargetMethod) runtime managed
  {
  } // end of method D1`1::.ctor

  .method public newslot strict virtual instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(!T x,
                      class [mscorlib]System.AsyncCallback DelegateCallback,
                      object DelegateAsyncState) runtime managed
  {
  } // end of method D1`1::BeginInvoke

  .method public newslot strict virtual instance void 
          EndInvoke(class [mscorlib]System.IAsyncResult DelegateAsyncResult) runtime managed
  {
  } // end of method D1`1::EndInvoke

  .method public newslot strict virtual instance void 
          Invoke(!T x) runtime managed
  {
  } // end of method D1`1::Invoke

} // end of class D1`1

.class public auto ansi C2`1<T>
       extends [mscorlib]System.Object
{
  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method C2`1::.ctor

} // end of class C2`1

.class public auto ansi C3
       extends class C2`1<class [NotReferenced]C1 modreq(int8)[]>
{
  .field public class C2`1<class [NotReferenced]C1 modreq(int8)[]> F1
  .field private class D1`1<class [NotReferenced]C1 modreq(int8)[]> E1Event
  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void class C2`1<class [NotReferenced]C1 modreq(int8)[]>::.ctor()
    IL_0006:  ret
  } // end of method C3::.ctor

  .method public instance void  M1(class C2`1<class [NotReferenced]C1 modreq(int8)[]> x) cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method C3::M1

  .method public specialname instance void 
          add_E1(class D1`1<class [NotReferenced]C1 modreq(int8)[]> obj) cil managed synchronized
  {
    // Code size       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class D1`1<class [NotReferenced]C1 modreq(int8)[]> C3::E1Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                            class [mscorlib]System.Delegate)
    IL_000d:  castclass  class D1`1<class [NotReferenced]C1 modreq(int8)[]>
    IL_0012:  stfld      class D1`1<class [NotReferenced]C1 modreq(int8)[]> C3::E1Event
    IL_0017:  ret
  } // end of method C3::add_E1

  .method public specialname instance void 
          remove_E1(class D1`1<class [NotReferenced]C1 modreq(int8)[]> obj) cil managed synchronized
  {
    // Code size       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.0
    IL_0002:  ldfld      class D1`1<class [NotReferenced]C1 modreq(int8)[]> C3::E1Event
    IL_0007:  ldarg.1
    IL_0008:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,
                                                                                           class [mscorlib]System.Delegate)
    IL_000d:  castclass  class D1`1<class [NotReferenced]C1 modreq(int8)[]>
    IL_0012:  stfld      class D1`1<class [NotReferenced]C1 modreq(int8)[]> C3::E1Event
    IL_0017:  ret
  } // end of method C3::remove_E1

  .method public specialname instance class C2`1<class [NotReferenced]C1 modreq(int8)[]> 
          get_P1() cil managed
  {
    // Code size       6 (0x6)
    .maxstack  1
    .locals init (class C2`1<class [NotReferenced]C1 modreq(int8)[]> V_0)
    IL_0000:  ldnull
    IL_0001:  stloc.0
    IL_0002:  br.s       IL_0004

    IL_0004:  ldloc.0
    IL_0005:  ret
  } // end of method C3::get_P1

  .method public specialname instance void 
          set_P1(class C2`1<class [NotReferenced]C1 modreq(int8)[]> 'value') cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method C3::set_P1

  .event class D1`1<class [NotReferenced]C1 modreq(int8)[]> E1
  {
    .addon instance void C3::add_E1(class D1`1<class [NotReferenced]C1 modreq(int8)[]>)
    .removeon instance void C3::remove_E1(class D1`1<class [NotReferenced]C1 modreq(int8)[]>)
  } // end of event C3::E1
  .property instance class C2`1<class [NotReferenced]C1 modreq(int8)[]>
          P1()
  {
    .get instance class C2`1<class [NotReferenced]C1 modreq(int8)[]> C3::get_P1()
    .set instance void C3::set_P1(class C2`1<class [NotReferenced]C1 modreq(int8)[]>)
  } // end of property C3::P1
} // end of class C3

.class public auto ansi beforefieldinit Microsoft.VisualC.StlClr.Generic.ContainerRandomAccessIterator`1<TValue>
       extends [mscorlib]System.Object
{
}

.class interface public abstract auto ansi beforefieldinit Microsoft.VisualC.StlClr.IVector`1<TValue>
{
  .method public hidebysig newslot abstract virtual 
          instance !TValue&  front() cil managed
  {
  } // end of method IVector`1::front

.method public hidebysig newslot abstract virtual 
          instance void modreq([mscorlib]System.Runtime.CompilerServices.IsUdtReturn) 
          begin(class Microsoft.VisualC.StlClr.Generic.ContainerRandomAccessIterator`1<!TValue>& A_1) cil managed
  {
  } // end of method IVector`1::begin
}

///////////////////////////////

.class public abstract auto ansi beforefieldinit X
       extends [mscorlib]System.Object
{
  .method public hidebysig newslot specialname abstract virtual 
          instance int32&  get_Token() cil managed
  {
  } // end of method X::get_Token

  .property instance int32& Token()
  {
    .get instance int32& X::get_Token()
  } // end of property X::Token
} // end of class X

]]>


            Dim compilation1 = CompilationUtils.CreateCompilationWithCustomILSource(
    <compilation name="Test">
        <file name="a.vb">
        </file>
    </compilation>, iLSource)

            Dim c3 = compilation1.GetTypeByMetadataName("C3")

            Assert.Null(c3.GetUseSiteErrorInfo())
            Assert.False(c3.HasUnsupportedMetadata)
            Assert.False(c3.ContainingSymbol.HasUnsupportedMetadata)

            Dim f1 = c3.GetMembers("F1").Single()
            Assert.NotNull(f1.GetUseSiteErrorInfo())
            Assert.True(f1.HasUnsupportedMetadata)

            Dim m1 = c3.GetMembers("M1").Single()
            Assert.NotNull(m1.GetUseSiteErrorInfo())
            Assert.True(m1.HasUnsupportedMetadata)

            Dim x = DirectCast(m1, MethodSymbol).Parameters(0)
            Assert.Null(x.GetUseSiteErrorInfo())
            Assert.True(x.HasUnsupportedMetadata)

            Dim e1 = c3.GetMembers("E1").Single()
            Assert.NotNull(e1.GetUseSiteErrorInfo())
            Assert.True(e1.HasUnsupportedMetadata)

            Dim p1 = c3.GetMembers("P1").Single()
            Assert.NotNull(p1.GetUseSiteErrorInfo())
            Assert.True(p1.HasUnsupportedMetadata)

            Dim intPtr = compilation1.GetTypeByMetadataName("System.IntPtr")

            Assert.True(intPtr.GetMembers("ToPointer").Single().HasUnsupportedMetadata)

            For Each m In intPtr.GetMembers("op_Explicit")
                Dim method = DirectCast(m, MethodSymbol)
                If method.ReturnType.IsErrorType() OrElse method.Parameters(0).Type.IsErrorType() Then
                    Assert.True(m.HasUnsupportedMetadata)
                Else
                    Assert.False(m.HasUnsupportedMetadata)
                End If
            Next

            ''

            Dim vector = compilation1.GetTypeByMetadataName("Microsoft.VisualC.StlClr.IVector`1")
            'unsupported MD in members doesn't propagate up to the type.
            Assert.False(vector.HasUnsupportedMetadata)

            'unsupported MD in the return type should propagate up to the method.
            Dim begin = vector.GetMember("begin")
            Assert.True(begin.HasUnsupportedMetadata)

            ''

            Dim typeX = compilation1.GetTypeByMetadataName("X")
            'unsupported MD in members doesn't propagate up to the type.
            Assert.False(typeX.HasUnsupportedMetadata)
        End Sub

        ''' <summary>
        ''' Throw a (handled) BadImageFormatException from MetadataDecoder
        ''' for a TypeRef resolution scope with Nil reference.
        ''' </summary>
        <Fact>
        <WorkItem(217689, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?_a=edit&id=217689")>
        <WorkItem(11795, "https://github.com/dotnet/roslyn/issues/11795")>
        Public Sub ResolutionScopeNilRef()
            Dim options = TestOptions.ReleaseDll.WithDeterministic(True)
            Dim comp1 = CreateCompilationWithMscorlib40(
{"Public Class A
End Class"},
                options:=options,
                assemblyName:="4D94B345-92CE-46BB-891E-048109648A0A")
            comp1.VerifyDiagnostics()
            Dim bytes1 = comp1.EmitToArray()
            Dim ref1 = AssemblyMetadata.CreateFromImage(bytes1).GetReference()

            Dim comp2 = CreateCompilationWithMscorlib40(
{"Public Class B
    Inherits A
End Class"},
                options:=options,
                assemblyName:="D4954046-BDDC-42C4-98F8-7E5573C16C33",
                references:={ref1})
            comp2.VerifyDiagnostics()
            Dim bytes2 = comp2.EmitToArray()

            ' Construct an unexpected assembly resolution scope from the valid compilation.
            ' Metadata contains a single TypeRef with assembly resolution scope where the
            ' scope is AssemblyRef 2: specifically the TypeRef to base class A. Replace that
            ' assembly resolution scope with AssemblyRef 0 (the unexpected value).
            Dim source3 =
"Class C
    Shared Sub Main()
        Dim o As New B()
    End Sub
End Class"
            ' In the TypeRef signature below, 10 is the assembly scope where (AssemblyRef << 2 | 2).
            ' (To verify, break in MetadataWriter.SerializeTypeRefTable when emitting comp2.)
            Dim validAssemblyRefTypeRef As Byte() = {10, 0, 82, 0, 0, 0}

            Dim invalidAssemblyRefTypeRef As Byte() = {2, 0, 82, 0, 0, 0} ' 2 is (AssemblyRef << 2 | 2)
            Dim ref2 = AssemblyMetadata.CreateFromImage(ReplaceBytes(bytes2, validAssemblyRefTypeRef, invalidAssemblyRefTypeRef)).GetReference()
            Dim comp3 = CreateCompilationWithMscorlib40({source3}, options:=options, references:={ref2})
            comp3.VerifyDiagnostics()
            Dim tree = comp3.SyntaxTrees(0)
            Dim model = comp3.GetSemanticModel(comp3.SyntaxTrees(0))
            Dim decl = tree.GetRoot().DescendantNodes.OfType(Of ObjectCreationExpressionSyntax).Single()
            Dim type = DirectCast(model.GetTypeInfo(decl).Type, TypeSymbol)
            Assert.Equal("B", type.ToTestDisplayString())
            Assert.False(type.IsErrorType())
            Assert.True(type.BaseType.IsErrorType()) ' Handled exception decoding base type TypeRef.

            ' As above but with nil ModuleRef.
            Dim invalidModuleRefTypeRef As Byte() = {1, 0, 82, 0, 0, 0} ' 1 is (ModuleRef << 2 | 1)
            ref2 = AssemblyMetadata.CreateFromImage(ReplaceBytes(bytes2, validAssemblyRefTypeRef, invalidModuleRefTypeRef)).GetReference()
            comp3 = CreateCompilationWithMscorlib40({source3}, options:=options, references:={ref2})
            comp3.VerifyDiagnostics()
            tree = comp3.SyntaxTrees(0)
            model = comp3.GetSemanticModel(tree)
            decl = tree.GetRoot().DescendantNodes.OfType(Of ObjectCreationExpressionSyntax).Single()
            type = DirectCast(model.GetTypeInfo(decl).Type, TypeSymbol)
            Assert.Equal("B", type.ToTestDisplayString())
            Assert.False(type.IsErrorType())
            Assert.True(type.BaseType.IsErrorType()) ' Handled exception decoding base type TypeRef.

            ' As above but with nil TypeRef.
            Dim invalidTypeRefTypeRef As Byte() = {3, 0, 82, 0, 0, 0} ' 3 is (TypeRef << 2 | 3)
            ref2 = AssemblyMetadata.CreateFromImage(ReplaceBytes(bytes2, validAssemblyRefTypeRef, invalidTypeRefTypeRef)).GetReference()
            comp3 = CreateCompilationWithMscorlib40({source3}, options:=options, references:={ref2})
            comp3.VerifyDiagnostics()
            tree = comp3.SyntaxTrees(0)
            model = comp3.GetSemanticModel(tree)
            decl = tree.GetRoot().DescendantNodes.OfType(Of ObjectCreationExpressionSyntax).Single()
            type = DirectCast(model.GetTypeInfo(decl).Type, TypeSymbol)
            Assert.Equal("B", type.ToTestDisplayString())
            Assert.False(type.IsErrorType())
            Assert.True(type.BaseType.IsErrorType()) ' Handled exception decoding base type TypeRef.
        End Sub

        Private Shared Function ReplaceBytes(bytes As ImmutableArray(Of Byte), before As Byte(), after As Byte()) As ImmutableArray(Of Byte)
            Dim index = IndexOfBytes(bytes, before, 0)
            Debug.Assert(index >= 0)
            Debug.Assert(IndexOfBytes(bytes, before, index + 1) < 0)
            Dim builder = ArrayBuilder(Of Byte).GetInstance()
            builder.AddRange(bytes.Take(index))
            builder.AddRange(after)
            builder.AddRange(bytes.Skip(index + before.Length))
            Return builder.ToImmutableAndFree()
        End Function

        Private Shared Function IndexOfBytes(bytes As ImmutableArray(Of Byte), pattern As Byte(), startIndex As Integer) As Integer
            Dim n = bytes.Length
            Dim m = pattern.Length
            For i = startIndex To n - m - 1
                For j = 0 To m - 1
                    If bytes(i + j) <> pattern(j) Then
                        GoTo EndOfLoop
                    End If
                Next
                Return i
EndOfLoop:
            Next
            Return -1
        End Function

    End Class

End Namespace

